#include "io.h"
#include "queue.h"
#include "unistd.h"
#include "shell.h"
#include "debug.h"
#include "semaphore.h"
#include "hal.h"

struct uart_gpio
{
    uint32_t            tx_gpio_clk;
    GPIO_TypeDef        *tx_gpio;
    uint16_t            tx_pin;
    uint8_t             tx_pin_source;
    uint8_t             tx_gpio_af;
    
    uint32_t            rx_gpio_clk;
    GPIO_TypeDef        *rx_gpio;
    uint16_t            rx_pin;
    uint8_t             rx_pin_source;
    uint8_t             rx_gpio_af;
};

struct uart_conf
{
    USART_InitTypeDef   hw_config;
    
    bool                irq_enable;
};

struct uart
{
    USART_TypeDef       *uart_port;
    uint32_t            uart_clk;
    int                 uart_irq;
    
    struct uart_gpio    *uart_gpio;
    
    struct uart_conf    *uart_config;

    queue_t             *rx_queue;
};

static const struct uart_gpio uart3_gpio = 
{
    RCC_AHBPeriph_GPIOC,
    GPIOC,
    GPIO_Pin_10,
    GPIO_PinSource10,
    GPIO_AF_USART3,
    
    RCC_AHBPeriph_GPIOC,
    GPIOC,
    GPIO_Pin_11,
    GPIO_PinSource11,
    GPIO_AF_USART3,    
};

static const struct uart_conf uart3_conf = 
{
    {
        115200,
        USART_WordLength_8b,
        USART_StopBits_1,
        USART_Parity_No,
        USART_Mode_Rx | USART_Mode_Tx,
        USART_HardwareFlowControl_None
    },
    
    true 
};

static queue_t uart1_queue, uart3_queue;
static sem_t uart1_sem;    
    
static const struct uart uart3 = 
{
    USART3,
    RCC_APB1Periph_USART3,
    USART3_IRQn,
    
    (struct uart_gpio *)&uart3_gpio,
    (struct uart_conf *)&uart3_conf,
    
    &uart3_queue
};
    
static const struct uart uart1 = 
{
    NULL,
    0,
    0,
    
    0,
    0,
    
    &uart1_queue
};

INLINE void __uart_clk_init(struct uart *uart)
{
    if (uart->uart_clk == RCC_APB2Periph_USART1)
        RCC_APB2PeriphClockCmd(uart->uart_clk, ENABLE);
    else
        RCC_APB1PeriphClockCmd(uart->uart_clk, ENABLE);
}

INLINE void __uart_gpio_init(struct uart *uart)
{
    GPIO_InitTypeDef GPIO_InitStructure;
     
    RCC_AHBPeriphClockCmd(uart->uart_gpio->rx_gpio_clk, ENABLE);
    RCC_AHBPeriphClockCmd(uart->uart_gpio->tx_gpio_clk, ENABLE);   
    
    GPIO_InitStructure.GPIO_Pin   = uart->uart_gpio->tx_pin;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_Init(uart->uart_gpio->tx_gpio, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = uart->uart_gpio->rx_pin;
    GPIO_Init(uart->uart_gpio->rx_gpio, &GPIO_InitStructure);
    
    GPIO_PinAFConfig(uart->uart_gpio->tx_gpio, uart->uart_gpio->tx_pin_source, uart->uart_gpio->tx_gpio_af);
    GPIO_PinAFConfig(uart->uart_gpio->rx_gpio, uart->uart_gpio->rx_pin_source, uart->uart_gpio->rx_gpio_af); 
}

INLINE void __uart_nvic_init(struct uart *uart)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel                   = uart->uart_irq;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    
    NVIC_Init(&NVIC_InitStructure);
}

INLINE void __uart_init(struct uart *uart)
{   
    queue_init(&uart3_queue);
  
    USART_Init(uart->uart_port, &uart->uart_config->hw_config);
    
    if (uart->uart_config->irq_enable == true)
        USART_ITConfig(uart->uart_port, USART_IT_RXNE, ENABLE);
    
    USART_Cmd(uart->uart_port, ENABLE);      
}

void USART3_IRQHandler(void)
{
    volatile char data, ruin;
  
    data = USART3->DR;    
    ruin = USART3->SR;
    
    queue_push(&uart3_queue, data); 
}

void USART1_IRQHandler(void)
{
    volatile char data;
    volatile int ruin;
  
    data = USART1->DR;
    ruin = USART1->SR;
    
    queue_push(&uart1_queue, data);
    sem_post(&uart1_sem);
}

static err_t uart_open (struct stdops *ops)
{
    struct uart *uart = (struct uart *)ops->priv;
    
    if (uart->uart_port)
    {
        __uart_clk_init(uart);
        __uart_gpio_init(uart);
        __uart_nvic_init(uart);
        __uart_init(uart);
    }
    
    return 0;
}

static ssize_t uart_read ( struct stdops *ops, char __user *buffer, size_t size, loff_t loff )
{   
    char *recv_point = buffer;
    os_u32 data;
    struct uart *uart = (struct uart *)ops->priv;
    queue_t *rx_queue = uart->rx_queue;
    
    sem_wait(&uart1_sem);
    
    while (!queue_pop(rx_queue, &data) && size)
    {
        size--;
        *recv_point++ = data;
    }
  
    return recv_point - buffer;
}

static ssize_t uart_write ( struct stdops *ops, const char __user *buffer, size_t size, loff_t loff )
{   
    while (size--)
    {
        while (USART_GetFlagStatus(USART_PRINT, USART_FLAG_TC) == RESET) {;}
  
        USART_SendData(USART_PRINT, *buffer++);
    }
  
    return size;
}

static struct stdops uart3_ops = 
{
    .open  = uart_open,
    .read  = uart_read,
    .write = uart_write,
    .priv  = (void *)&uart3
};

static struct stdops uart1_ops = 
{
    .open  = uart_open,
    .read  = uart_read,
    .write = uart_write,
    .priv  = (void *)&uart1
};

static err_t uart_init(void)
{
    err_t err;
    
    err = device_register("uart3", &uart3_ops);
    ASSERT_KERNEL(err == 0);
    
    sem_init(&uart1_sem, 0, QUEUE_BUFFER_MAX);
    err = stdobj_create(STDOBJ_DEVICE, "uart1", &uart1_ops);
    ASSERT_KERNEL(err == 0);
    
    shell_dev_create("uart1", &err, SHELL_DEVICE_ACK);
    ASSERT_KERNEL(err == 0);
    
    return err;
}
HAL_FUNC_EXPORT(uart_init, init the uart and its transceiving function, 1);
